#ifndef _CU_INT4_H_
#define _CU_INT4_H_

//**********************************************************************************
// int4 operators and functions
//**********************************************************************************
SDH_CALL int4   operator + (const int4 & u, const int v) { int4 r = { u.x + v, u.y + v, u.z + v, u.w + v }; return r; }
SDH_CALL int4   operator - (const int4 & u, const int v) { int4 r = { u.x - v, u.y - v, u.z - v, u.w - v }; return r; }
SDH_CALL int4   operator * (const int4 & u, const int v) { int4 r = { u.x * v, u.y * v, u.z * v, u.w * v }; return r; }
SDH_CALL int4   operator / (const int4 & u, const int v) { int4 r = { u.x / v, u.y / v, u.z / v, u.w / v }; return r; }
SDH_CALL int4   operator % (const int4 & u, const int v) { int4 r = { u.x % v, u.y % v, u.z % v, u.w % v }; return r; }

SDH_CALL int4   operator + (const int v, const int4 & u) { int4 r = { v + u.x, v + u.y, v + u.z, v + u.w }; return r; }
SDH_CALL int4   operator - (const int v, const int4 & u) { int4 r = { v - u.x, v - u.y, v - u.z, v - u.w }; return r; }
SDH_CALL int4   operator * (const int v, const int4 & u) { int4 r = { v * u.x, v * u.y, v * u.z, v * u.w }; return r; }
SDH_CALL int4   operator / (const int v, const int4 & u) { int4 r = { v / u.x, v / u.y, v / u.z, v / u.w }; return r; }
SDH_CALL int4   operator % (const int v, const int4 & u) { int4 r = { v % u.x, v % u.y, v % u.z, v % u.w }; return r; }

SDH_CALL int4   operator + (const int4 & u, const int4 & v) { int4 r = { u.x + v.x, u.y + v.y, u.z + v.z, u.w + v.w }; return r; }
SDH_CALL int4   operator - (const int4 & u, const int4 & v) { int4 r = { u.x - v.x, u.y - v.y, u.z - v.z, u.w - v.w }; return r; }
SDH_CALL int4   operator * (const int4 & u, const int4 & v) { int4 r = { u.x * v.x, u.y * v.y, u.z * v.z, u.w * v.w }; return r; }
SDH_CALL int4   operator / (const int4 & u, const int4 & v) { int4 r = { u.x / v.x, u.y / v.y, u.z / v.z, u.w / v.w }; return r; }
SDH_CALL int4   operator % (const int4 & u, const int4 & v) { int4 r = { u.x % v.x, u.y % v.y, u.z % v.z, u.w % v.w }; return r; }

SDH_CALL int4 & operator += (int4 & u, const int4 & v) { u.x += v.x; u.y += v.y; u.z += v.z; u.w += v.w; return u; }
SDH_CALL int4 & operator -= (int4 & u, const int4 & v) { u.x -= v.x; u.y -= v.y; u.z -= v.z; u.w -= v.w; return u; }
SDH_CALL int4 & operator *= (int4 & u, const int4 & v) { u.x *= v.x; u.y *= v.y; u.z *= v.z; u.w *= v.w; return u; }
SDH_CALL int4 & operator /= (int4 & u, const int4 & v) { u.x /= v.x; u.y /= v.y; u.z /= v.z; u.w /= v.w; return u; }
SDH_CALL int4 & operator %= (int4 & u, const int4 & v) { u.x %= v.x; u.y %= v.y; u.z %= v.z; u.w %= v.w; return u; }

SDH_CALL int4 & operator += (int4 & u, const int v) { u.x += v; u.y += v; u.z += v; u.w += v; return u; }
SDH_CALL int4 & operator -= (int4 & u, const int v) { u.x -= v; u.y -= v; u.z -= v; u.w -= v; return u; }
SDH_CALL int4 & operator *= (int4 & u, const int v) { u.x *= v; u.y *= v; u.z *= v; u.w *= v; return u; }
SDH_CALL int4 & operator /= (int4 & u, const int v) { u.x /= v; u.y /= v; u.z /= v; u.w /= v; return u; }
SDH_CALL int4 & operator %= (int4 & u, const int v) { u.x %= v; u.y %= v; u.z %= v; u.w %= v; return u; }

SDH_CALL int4   operator - (const int4 & v) { int4 r = { -v.x, -v.y, -v.z, -v.w }; return r; }

SDH_CALL int3 xyz        (const int4 & v) { int3 u = { v.x, v.y, v.z }; return u; }

SDH_CALL int4 catmullrom (const int4 & P0, const int4 & P1, const int4 & P2, const int4 & P3, float t)
{
  const float ts = t * t;
  const float tc = t * ts;

  return (P0 * (-tc + 2.0f * ts - t) + P1 * (3.0f * tc - 5.0f * ts + 2.0f) + P2 * (-3.0f * tc + 4.0f * ts + t) + P3 * ( tc - ts)) * 0.5f;
}

SDH_CALL int4 lerp      (const int4 & u, const int4 & v, float t ) { return u + t * (v - u); }

SDH_CALL int  dot       (const int4 & u, const int4 & v) { return (u.x*v.x + u.y*v.y + u.z*v.z + u.w*v.w); }
SDH_CALL int  dot       (const int4 & u, const int3 & v) { return (u.x*v.x + u.y*v.y + u.z*v.z + u.w); }
SDH_CALL int  dot       (const int3 & u, const int4 & v) { return (u.x*v.x + u.y*v.y + u.z*v.z + v.w); }
SDH_CALL int  dot3      (const int4 & u, const int4 & v) { return (u.x*v.x + u.y*v.y + u.z*v.z); }
SDH_CALL int  dot3      (const int4 & u, const int3 & v) { return (u.x*v.x + u.y*v.y + u.z*v.z); }
SDH_CALL int  dot3      (const int3 & u, const int4 & v) { return (u.x*v.x + u.y*v.y + u.z*v.z); }

SDH_CALL int4 clamp     (const int4 & u, int a, int b) { int4 r = { clamp(u.x, a, b), clamp(u.y, a, b), clamp(u.z, a, b), clamp(u.w, a, b) }; return r; }

SDH_CALL int4 min       (const int4 & u, const int4 & v) { int4 r = {min(u.x, v.x), min(u.y, v.y), min(u.z, v.z), min(u.w, v.w)}; return r; }
SDH_CALL int4 max       (const int4 & u, const int4 & v) { int4 r = {max(u.x, v.x), max(u.y, v.y), max(u.z, v.z), max(u.w, v.w)}; return r; }
SDH_CALL int3 min3      (const int4 & u, const int4 & v) { int3 r = {min(u.x, v.x), min(u.y, v.y), min(u.z, v.z)}; return r; }
SDH_CALL int3 max3      (const int4 & u, const int4 & v) { int3 r = {max(u.x, v.x), max(u.y, v.y), max(u.z, v.z)}; return r; }

SDH_CALL int  sad       (const int4 & u, const int4 & v) { return (abs(u.x - v.x) + abs(u.y - v.y) + abs(u.z - v.z) + abs(u.w - v.w)); } // Sum of Absolute Differences for xyzw components
SDH_CALL int  ssd       (const int4 & u, const int4 & v) { return (sqr(u.x - v.x) + sqr(u.y - v.y) + sqr(u.z - v.z) + sqr(u.w - v.w)); } // Sum of Squared Differences for xyzw components
SDH_CALL int  sad3      (const int4 & u, const int4 & v) { return (abs(u.x - v.x) + abs(u.y - v.y) + abs(u.z - v.z)); } // Sum of Absolute Differences for xyz components only
SDH_CALL int  ssd3      (const int4 & u, const int4 & v) { return (sqr(u.x - v.x) + sqr(u.y - v.y) + sqr(u.z - v.z)); } // Sum of Squared  Differences for xyz components only

SDH_CALL int4 set(int4 & v, int x = 0, int y = 0, int z = 0, int w = 0) 
{ 
  v.x = x; v.y = y; v.z = z; v.w = w; return v; 
}
SDH_CALL int4 set(int4 & v, int3 xyz, int w = 0) 
{ 
  v.x = xyz.x; v.y = xyz.y; v.z = xyz.z; v.w = w; return v; 
}
SDH_CALL int4 set(int4 & v, int2 xy, int z = 0, int w = 0) 
{
  v.x = xy.x; v.y = xy.y; v.z = z; v.w = w; return v; 
}

SDH_CALL int4 to_int4(int x) { int4 r = {x, x, x, x}; return r;}
SDH_CALL int4 to_int4(int x, int y, int z, int w) { int4 r = {x, y, z, w}; return r;}
SDH_CALL int4 to_int4(const int3 & u, int w) { int4 r = {u.x, u.y, u.z, w}; return r; }

SDH_CALL float  length (const int4 & u) { return sqrtf(sqr(u.x) + sqr(u.y) + sqr(u.z) + sqr(u.w)); }
SDH_CALL float  length3(const int4 & u) { return sqrtf(sqr(u.x) + sqr(u.y) + sqr(u.z)); }

SDH_CALL float4 sqrt   (const int4 & u) { float4 r = { sqrtf(u.x), sqrtf(u.y), sqrtf(u.z), sqrtf(u.w) }; return r; }
SDH_CALL float4 sqr    (const int4 & u) { float4 r = { sqr  (u.x), sqr  (u.y), sqr  (u.z), sqr  (u.w) }; return r; }

#endif